package com.ervanalb.cellradio.server;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import com.ervanalb.cellradio.shared.communicator.PacketSource;
import com.ervanalb.cellradio.shared.communicator.packets.Packet;

/**
 * Provides ServerSocketSources on request. Maintains a single listening
 * DatagramSocket. When a packet arrives, it parses the packet and sends it to
 * the appropriate ServerSocketSource (based on channel number.) In this way, a
 * Server can have multiple Communicators, each with their own SocketSource, but
 * sharing a single socket (a ServerSocket).
 * 
 * @author eric
 */
public class ServerSocket extends Thread {

	public static final long RX_TICK = 100;

	public DatagramSocket sock;
	protected DatagramPacket p;
	protected byte[] buffer;
	public Exception e;

	protected boolean go;

	public static final int DGRAM_BUF_SIZE = 1024;

	public Map<Integer, BlockingQueue<Packet>> rxQueues;

	public ServerSocket(int port) throws SocketException {
		sock = new DatagramSocket(port);
		buffer = new byte[DGRAM_BUF_SIZE];
		rxQueues = new HashMap<Integer, BlockingQueue<Packet>>();
		start();
	}

	public void run() {
		go = true;

		while (go) {
			p = new DatagramPacket(buffer, DGRAM_BUF_SIZE);
			try {
				sock.setSoTimeout((int) RX_TICK);
				sock.receive(p);
			} catch (SocketTimeoutException e) {
				continue;
			} catch (IOException e) {
				go = false;
				this.e = e;
				break;
			}

			Packet parsed = new Packet(p);
			if (rxQueues.containsKey(parsed.channel))
				try {
					rxQueues.get(parsed.channel).put(parsed);
				} catch (InterruptedException e) {
				}
		}
	}

	public void finish() {
		go = false;
		interrupt();
	}

	public ServerSocketSource addClient(int address) {
		rxQueues.put(address, new LinkedBlockingQueue<Packet>());
		return new ServerSocketSource(address);
	}

	class ServerSocketSource implements PacketSource {
		public int from;

		public ServerSocketSource(int from) {
			this.from = from;
		}

		@Override
		public Packet receive(long timeout) {
			try {
				return rxQueues.get(from).poll(timeout, TimeUnit.MILLISECONDS);
			} catch (InterruptedException e) {
				return null;
			}
		}
	}
}
